package main

import (
	"strings"
	"sync"
)

const Split = "||"

type Grant struct {
	Action string
	Object string
}

func (g *Grant) marshal() string {
	return g.Action + Split + g.Object
}

func (g *Grant) unmarshal(s string) {
	ss := strings.Split(s, Split)
	g.Action = ss[0]
	g.Object = ss[1]
}

type Privilege struct {
	mu    sync.Mutex
	roles map[string]*sync.Map
}

func NewPrivilege() *Privilege {
	return &Privilege{roles: make(map[string]*sync.Map)}
}

func (p *Privilege) Auth(role string, g *Grant) bool {
	r := p.getRole(role)
	_, ok := r.Load(g.marshal())
	return ok
}

func (p *Privilege) Add(role string, grants ...*Grant) {
	r := p.getRole(role)
	for i := range grants {
		r.Store(grants[i].marshal(), nil)
	}
}

func (p *Privilege) Get(role string) []*Grant {
	grants := make([]*Grant, 0)
	r := p.getRole(role)
	r.Range(func(key interface{}, _ interface{}) bool {
		s := key.(string)
		grant := &Grant{}
		grant.unmarshal(s)
		grants = append(grants, grant)
		return true
	})
	return grants
}

func (p *Privilege) Del(role string, grants ...*Grant) {
	if len(grants) == 0 {
		p.delRole(role)
	} else {
		r := p.getRole(role)
		for i := range grants {
			r.Delete(grants[i].marshal())
		}
	}
}

func (p *Privilege) GetRoles() []string {
	roles := make([]string, 0)
	p.mu.Lock()
	defer p.mu.Unlock()
	for role := range p.roles {
		roles = append(roles, role)
	}
	return roles
}

func (p *Privilege) getRole(role string) *sync.Map {
	p.mu.Lock()
	defer p.mu.Unlock()
	s, ok := p.roles[role]
	if !ok {
		s = &sync.Map{}
		p.roles[role] = s
	}
	return s
}

func (p *Privilege) delRole(role string) {
	p.mu.Lock()
	defer p.mu.Unlock()
	p.roles[role] = &sync.Map{}
}
